-module(erl_sql_compare).
-feature(maybe_expr, enable).

-author("zhuiyi9009@163.com").

-include("common.hrl").

%% API
-export([start/2, start/3]).

%% "file:source.sql"
%% "mysql:test:test@127.0.0.1:3306/test_1?charset=utf8&abc=100"
start(Source, Dest) ->
    start(Source, Dest, []).

-type compare_option() ::
    {output, result | File :: string() | mysql}.
-spec start(Source, Dest, Options) -> ok | no_change | {ok, Lines} | {error, Reason} when
    Source :: string(),
    Dest :: string(),
    Options :: [compare_option()],
    Reason :: any(),
    Lines :: [string()].
start(Source, Dest, Options) ->
    maybe
        ok ?= ensure_options(Options),
        {ok, SourceSchema} ?= parse_sql_target(#schema_sync{url = Source, side = ?COMPARE_SIDE_SOURCE}),
        {ok, DestSchema} ?= parse_sql_target(#schema_sync{url = Dest, side = ?COMPARE_SIDE_DEST}),
        case erl_sql_compare_diff:get_schema_diff(SourceSchema, DestSchema) of
            [] ->
                ?WARN("compare found no change ~n", []),
                no_change;
            DiffLines ->
                do_output(SourceSchema, DestSchema, DiffLines, Options)
        end
    else
        {error, Reason} ->
            ?WARN("compare error ~w~n", [Reason]),
            {error, Reason};
        Other ->
            ?WARN("compare unknown error ~w~n", [Other]),
            {error, Other}
    end.

parse_sql_target(#schema_sync{url = "file:" ++ File} = Schema) ->
    erl_sql_compare_parse:parse_from_file(Schema, File);
parse_sql_target(#schema_sync{url = "mysql:" ++ Dsn} = Schema) ->
    erl_sql_compare_parse:parse_from_mysql(Schema, Dsn);
parse_sql_target(Other) ->
    ?WARN("target not support ~s~n", [Other]),
    {error, error_sql_target}.

ensure_options([]) ->
    ok;
ensure_options([{output, OutPut}|Rest]) ->
    case OutPut of
        result ->
            ensure_options(Rest);
        mysql ->
            ensure_options(Rest);
        File when is_list(File) ->
            ensure_options(Rest);
        _ ->
            {error_option, {output, OutPut}}
    end;
ensure_options([Option|_]) ->
    {unknown_option, Option}.

do_output(_SourceSchema, DestSchema, DiffLines, Options) ->
    case proplists:get_value(output, Options) of
        result ->
            {ok, DiffLines};
        mysql ->
            case DestSchema of
                #schema_sync{type = mysql, url = Url} ->
                    PoolId = erl_sql_compare_db:pool_id(DestSchema),
                    ?DEBUG("write diff to ~s~n", [Url]),
                    case erl_sql_compare_db:execute(PoolId, DiffLines) of
                        {ok, _} ->
                            ok;
                        Other ->
                            Other
                    end;
                _ ->
                    {error, "dest not mysql type"}
            end;
        File ->
            ?DEBUG("write diff to ~s~n", [File]),
            file:write_file(File, DiffLines)
    end.


